var mysql = require('mysql');
var config = require('./config');
var net_config = config.net;
var sof = mysql.createConnection(config.mysql);
var brain = require('brain');
var Q = require('q');
var inquirer = require("inquirer");
var fs = require('fs');

var path = net_config.data_path;
var max_query = net_config.origin_data_set.max_query;
var min_view = net_config.origin_data_set.min_view;
var min_votes = net_config.origin_data_set.min_votes;

var batch_test_config = net_config.batch_test;

var net = new brain.NeuralNetwork(net_config.neural_network_config);
var train_list = [];

(function main() {
    // choose func
    var func = check_local_data() ? read_data_from_file : query_questions;
    Q.fcall(mysql_connect, sof)
        .then(func)
        .catch(function (error) {
            console.error(error);
        })
        .done(function (query) {

            if (!query.is_from_file) {
                train_list = format_data(query);
                train(train_list);

                // save to file
                fs.writeFile(path, JSON.stringify(train_list), function (err) {
                    if (err) {
                        return console.log(err);
                    }
                    console.log("The data was saved!");
                });
            } else {
                train_list = query.data;
                train(train_list);
            }

            software_interface(query);
        })
})();

function check_local_data() {
    try {
        stats = fs.lstatSync(path);

        if (stats.isFile()) {
            return true;
        }

        return false;
    }
    catch (error) {
        console.log("there no data file.");
        return false;
    }
}

function read_data_from_file(query) {
    var deferred = Q.defer();
    var conn = query.conn;

    fs.readFile(path, function (err, data) {
        if (err) {
            console.error('error query: ' + err.stack);
            deferred.reject(new Error("query fail!"));
        } else {
            deferred.resolve({
                conn: conn,
                data: JSON.parse(data.toString()),
                is_from_file: true
            });
        }
    });

    return deferred.promise;
}

function mysql_connect(conn) {
    var deferred = Q.defer();

    if (conn) {
        conn.connect(function (err) {
            if (err) {
                console.error('error connecting: ' + err.stack);
                deferred.reject(new Error("connect fail!"));
            } else {
                console.log('successfully connect to the db.');
                deferred.resolve({
                    conn: conn,
                    data: null
                });
            }
        });
    } else {
        reject(new Error("no config object!"));
    }

    return deferred.promise;
}

function mysql_connect_finish(conn) {
    if (conn) {
        console.log('successfully close the connection to the db.');
        conn.end();
    }
}

function query_questions(query) {
    var deferred = Q.defer();
    var conn = query.conn;

    conn.query('SELECT * FROM questions ' +
        'WHERE view >= ' + min_view + ' AND  votes >= ' + min_votes  + ' ' +
        'LIMIT ' + max_query, function (err, rows, fields) {
        if (err) {
            console.error('error query: ' + err.stack);
            deferred.reject(new Error("query fail!"));
        } else {
            var funcs = [];
            for (var key in rows) {
                funcs.push(query_tag_ids_by_question_id);
            }

            for (var key in rows) {
                funcs.push(query_answers_by_question_id);
            }

            funcs.push(function (query) {
                var conn = query.conn;
                var data = query.data;

                deferred.resolve({
                    conn: conn,
                    data: rows
                });
            });

            var result = Q({
                conn: conn,
                id: 0,
                data: rows
            });

            funcs.forEach(function (f) {
                result = result.then(f);
            });
        }
    });

    return deferred.promise;
}

function query_question_by_id(query) {
    var deferred = Q.defer();
    var conn = query.conn;
    var quz_id = query.question_id;
    var min_votes = batch_test_config.min_votes;

    conn.query('SELECT * FROM questions ' +
        'WHERE question_id = ' + quz_id + ' AND votes >= ' + min_votes, function (err, rows, fields) {
        if (err) {
            console.error('error query: ' + err.stack);
            deferred.reject(new Error("query fail!"));
        } else {
            var funcs = [];
            for (var key in rows) {
                funcs.push(query_tag_ids_by_question_id);
            }

            for (var key in rows) {
                funcs.push(query_answers_by_question_id);
            }

            funcs.push(function (query) {
                var conn = query.conn;
                var data = query.data;

                deferred.resolve({
                    conn: conn,
                    data: rows
                });
            });

            var result = Q({
                conn: conn,
                id: 0,
                data: rows
            });

            funcs.forEach(function (f) {
                result = result.then(f);
            });
        }
    });

    return deferred.promise;
}

function query_tag_ids_by_question_id(query) {
    var deferred = Q.defer();
    var conn = query.conn;
    var id = query.id;
    var data = query.data;
    var quz_id = data[id].question_id;

    conn.query('SELECT * FROM questions_tags ' +
        'WHERE question_id = ' + quz_id, function (err, rows, fields) {
        if (err) {
            console.error('error query: ' + err.stack);
            deferred.reject(new Error("query fail!"));
        } else {
            data[id].tags = rows;
            deferred.resolve({
                conn: conn,
                id: (data.length == id + 1) ? 0 : id + 1,
                data: data
            });
        }
    });
    return deferred.promise;
}

function query_answers_by_question_id(query) {
    var deferred = Q.defer();
    var conn = query.conn;
    var id = query.id;
    var data = query.data;
    var quz_id = data[id].question_id;

    conn.query('SELECT * FROM answers ' +
        'WHERE question_id = ' + quz_id + ' ' +
        'ORDER BY votes DESC', function (err, rows, fields) {
        if (err) {
            console.error('error query: ' + err.stack);
            deferred.reject(new Error("query fail!"));
        } else {
            data[id].answers = rows;

            // define queryt
            var funcs = [];
            for (var key in rows) {
                funcs.push(query_user_by_id);
            }

            funcs.push(function (query) {
                var conn = query.conn;
                var data = query.data;

                deferred.resolve({
                    conn: conn,
                    id: (data.length == id + 1) ? 0 : id + 1,
                    data: data
                });
            });

            var result = Q({
                conn: conn,
                id: 0,
                data: rows
            });

            funcs.forEach(function (f) {
                result = result.then(f);
            });

        }
    });

    return deferred.promise;
}

function query_user_by_id(query) {
    var deferred = Q.defer();
    var conn = query.conn;
    var id = query.id;
    var data = query.data;
    var user_id = data[id].user_id;

    conn.query('SELECT * FROM users ' +
        'WHERE user_id = ' + user_id, function (err, rows, fields) {
        if (err) {
            console.error('error query: ' + err.stack);
            deferred.reject(new Error("query fail!"));
        } else {
            data[id].user = {
                reputation: rows[0].reputation
            };

            Q.fcall(query_tag_ids_by_user_id, {
                conn: conn,
                data: {user_id: user_id}
            }).then(function (query) {
                var deferred = Q.defer();

                data[id].user.tags = query.data;
                deferred.resolve({
                    conn: conn,
                    data: {user_id: user_id}
                });

                return deferred.promise;
            }).then(query_reword_by_user_id)
                .then(function (query) {
                    data[id].user.reword = query.data;
                })
                .done(function () {
                    deferred.resolve({
                        conn: conn,
                        id: (data.length == id + 1) ? 0 : id + 1,
                        data: data
                    });
                });
        }
    });

    return deferred.promise;
}

function query_tag_ids_by_user_id(query) {
    var deferred = Q.defer();
    var conn = query.conn;
    var data = query.data;
    var user_id = data.user_id;

    conn.query('SELECT * FROM users_tags ' +
        'WHERE user_id = ' + user_id, function (err, rows, fields) {
        if (err) {
            console.error('error query: ' + err.stack);
            deferred.reject(new Error("query fail!"));
        } else {
            deferred.resolve({
                conn: conn,
                data: rows
            });
        }
    });

    return deferred.promise;
}

function query_reword_by_user_id(query) {
    var deferred = Q.defer();
    var conn = query.conn;
    var data = query.data;
    var user_id = data.user_id;

    var result = {
        bronze: 0,
        silver: 0,
        gold: 0
    };

    Q.fcall(function () {
        var deferred = Q.defer();

        conn.query('SELECT * FROM users_bronze ' +
            'WHERE user_id = ' + user_id, function (err, rows, fields) {
            if (err) {
                console.error('error query: ' + err.stack);
                deferred.reject(new Error("query fail!"));
            } else {
                if (rows.length > 0) {
                    result.bronze = rows.length;
                }
                deferred.resolve();
            }
        });

        return deferred.promise;
    }, result).then(function () {
        var deferred = Q.defer();

        conn.query('SELECT * FROM users_silver ' +
            'WHERE user_id = ' + user_id, function (err, rows, fields) {
            if (err) {
                console.error('error query: ' + err.stack);
                deferred.reject(new Error("query fail!"));
            } else {
                if (rows.length > 0) {
                    result.silver = rows.length;
                }
                deferred.resolve();
            }
        });

        return deferred.promise;
    }).then(function () {
        var deferred = Q.defer();

        conn.query('SELECT * FROM users_gold ' +
            'WHERE user_id = ' + user_id, function (err, rows, fields) {
            if (err) {
                console.error('error query: ' + err.stack);
                deferred.reject(new Error("query fail!"));
            } else {
                if (rows.length > 0) {
                    result.gold = rows.length;
                }
                deferred.resolve();
            }
        });

        return deferred.promise;
    }).done(function () {
        deferred.resolve({
            conn: conn,
            data: result
        });
    });

    return deferred.promise;
}

function format_data(query) {
    var data = query.data;
    var res = [];
    var great_answer_votes = 0;

    for (var i in data) {
        var quz = data[i];
        var temp_quz = {
            tags: []
        };

        var tags = quz.tags;
        for (var j in tags) {
            var tag_id = tags[j].tag_id;
            temp_quz.tags.push(tag_id);
        }

        var ans = quz.answers;
        for (var j in ans) {
            var temp_ans = {
                user: {
                    reputation: 0,
                    tags: [],
                    reword: {}
                }
            };

            temp_ans.user.reputation = ans[j].user.reputation;
            temp_ans.user.reword = ans[j].user.reword;
            var tags = ans[j].user.tags;
            for (var k in tags) {
                var tag_id = tags[k].tag_id;
                temp_ans.user.tags.push(tag_id);
            }

            var total = temp_quz.tags.length;
            var counter = 0;
            var quz_tags = temp_quz.tags;
            var user_tags = temp_ans.user.tags;
            for (var k in quz_tags) {
                tag_id = quz_tags[k];
                if (user_tags.indexOf(tag_id) != -1) {
                    counter++;
                }
            }
            var relativity = counter / total;

            var temp = {
                relativity: relativity,
                reputation: temp_ans.user.reputation / 1000000,
                reword_bronze: temp_ans.user.reword.bronze / 100,
                reword_silver: temp_ans.user.reword.silver / 100,
                reword_gold: temp_ans.user.reword.gold / 100
            };

            var output = 0;
            if (great_answer_votes <= ans[j].votes && ans[j].votes > 0) {
                great_answer_votes = ans[j].votes;
                output = 1;
            }

            var item = {
                input: temp,
                output: (output == 1) ? {best_answer: 1} : {not_best_answer: 1}
            };
            res.push(item);
        }
    }

    return res;
}

function software_interface(query) {
    var conn = query.conn;
    var data = query.data;

    function train_interface() {
        var questions = [
            {
                type: "input",
                name: "question_id",
                message: "What's the question's id?"
            }
        ];

        inquirer.prompt(questions, function (answers) {
            var quz_id = answers.question_id;

            console.log('please wait!');
            Q.fcall(query_question_by_id, {conn: conn, question_id: quz_id})
                .catch(function (error) {
                    console.error(error);
                    questions_interface();
                })
                .done(function (query) {
                    console.log('finished!');
                    var add_data = format_data(query);

                    train_list = train_list.concat(add_data);

                    // Do something to train
                    train(train_list);

                    questions_interface();
                });
        });
    }

    function test_interface() {
        var questions = [
            {
                type: "input",
                name: "question_id",
                message: "What's the question's id?"
            }
        ];

        inquirer.prompt(questions, function (answers) {
            var quz_id = answers.question_id;

            console.log('please wait!');
            Q.fcall(query_question_by_id, {conn: conn, question_id: quz_id})
                .catch(function (error) {
                    console.error(error);
                    questions_interface();
                })
                .done(function (query) {
                    console.log('finished!');

                    var data_list = format_data(query);

                    // Do something to test
                    var info = test(data_list);
                    console.log(info);

                    questions_interface();
                });
        });
    }

    function batch_test_interface() {

        console.log('please wait!');
        Q.fcall(batch_test, {conn: conn})
            .catch(function(error) {
                console.error(error);
                questions_interface();
            })
            .done(function(query) {
                console.log('finished!');
                questions_interface();
            });

    }

    function questions_interface() {
        var questions = [
            {
                type: "list",
                name: "action",
                message: "What do you want to do?",
                choices: [
                    "Train",
                    "Test",
                    "Batch Test",
                    "Quit"
                ]
            }
        ];

        inquirer.prompt(questions, function (answers) {
            switch (answers.action) {
                case "Train":
                    train_interface();
                    break;
                case "Test":
                    test_interface();
                    break;
                case "Batch Test":
                    batch_test_interface();
                    break;
                case "Quit":
                    // save train_list
                    save(train_list);
                    // close mysql connection
                    mysql_connect_finish(conn);
                    break;
                default :
                    questions_interface();
                    break;
            }
        });
    }

    // go into software_interface
    questions_interface();
}

function train(train_list) {
    net.train(train_list);
    console.log('training finished.');
}

function test(data_list) {
    var best_ans_array = [];
    for(var key in data_list) {
        if(data_list[key].output.best_answer) {
            best_ans_array.push(key);
        }
    }

    var the_best = -1;
    var the_best_record = 0;
    for(var key in data_list) {
        result = net.run(data_list[key].input);
        if(result.best_answer > the_best_record) {
            the_best = key;
            the_best_record = result.best_answer;
        }
    }

    var info = {
        prediction: the_best,
        actual: best_ans_array,
        result: (best_ans_array.indexOf(the_best) != -1) ? true : false
    };

    return info;
}

function batch_test(query) {
    var random_arr = [];
    var func_arr = [];
    var conn = query.conn;
    var count = 0;
    var begin = batch_test_config.begin;
    var end = batch_test_config.end;
    var size = batch_test_config.size;
    var min_votes = batch_test_config.min_votes;

    var test_result = {
        totalTest: 0,
        totalAccuracy: 0,
        totalAnswer: 0,
        accuracy: 0,
        gambling: 0
    };

    while(count < size) {
        var index = Math.floor(Math.random()*end+begin);
        if(random_arr.indexOf(index) == -1) {
            random_arr.push(index);
            count ++;
            func_arr.push(query_question_by_id);
            func_arr.push(adapter);
        }
    }

    func_arr.push(function() {
        test_result.accuracy = test_result.totalAccuracy / test_result.totalTest;
        test_result.gambling = 1 / (test_result.totalAnswer / test_result.totalTest);
        console.log('Accuracy: ', (test_result.accuracy * 100).toFixed(2), '\%');
        console.log('Gambling: ', (test_result.gambling * 100).toFixed(2), '\%');
    });

    function adapter(query) {
        var deferred = Q.defer();
        var conn = query.conn;
        var data = query.data;
        var quz_id = random_arr.shift();

        if(data.length > 0 && data[0].answers.length > 0) {
            test_result.totalTest ++;
            test_result.totalAnswer += data[0].answers.length;
            var data_list = format_data(query);
            var info = test(data_list);
            if(info.result) {
                test_result.totalAccuracy ++;
            }
        }

        deferred.resolve({
            conn: conn,
            question_id: quz_id
        });

        return deferred.promise;
    }

    var quz_id = random_arr.shift();
    var result = Q({conn: conn, question_id: quz_id});

    func_arr.forEach(function (f) {
        result = result.then(f);
    });

    return result;
}

function save(train_list) {
    // save to file
    fs.writeFile(path, JSON.stringify(train_list), function (err) {
        if (err) {
            return console.log(err);
        }
        console.log("The data was saved!");
    });
}